package org.example.controller

import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.mock.env.MockEnvironment
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.WebClientResponseException
import reactor.core.publisher.Mono
import spock.lang.Specification

class PingControllerSpec extends Specification {

    def webClient = Mock(WebClient)
    def environment = new MockEnvironment()
    def controller = new PingController(webClient, environment)

    def setup() {
        // 设置环境属性
        environment.setProperty('spring.application.name', 'ping-service')
    }

    def "should return pong response when pong service is available"() {
        given:
        WebClient.RequestHeadersUriSpec uriSpec = Mock()
        WebClient.ResponseSpec responseSpec = Mock()

        webClient.get() >> uriSpec
        uriSpec.uri('/pong') >> uriSpec
        uriSpec.header('X-Ping-Service-Name', 'ping-service') >> uriSpec
        uriSpec.retrieve() >> responseSpec
        responseSpec.bodyToMono(String) >> Mono.just('World')

        when:
        ResponseEntity<String> response = controller.sendRequestToPongService().block()

        then:
        response.statusCode == HttpStatus.OK
        response.body == 'World'
    }

    def "should handle too many requests exception from pong service"() {
        given:
        WebClient.RequestHeadersUriSpec uriSpec = Mock()
        WebClient.ResponseSpec responseSpec = Mock()
        def exception = new WebClientResponseException("Too Many Requests", 429, "Too Many Requests", null, null, null)
        webClient.get() >> uriSpec
        uriSpec.uri('/pong') >> uriSpec
        uriSpec.header('X-Ping-Service-Name', 'ping-service') >> uriSpec
        uriSpec.retrieve() >> responseSpec
        responseSpec.bodyToMono(String.class) >> Mono.error(exception)


        when:
        ResponseEntity<String> response = controller.sendRequestToPongService().block()

        then:
        response.statusCode == HttpStatus.TOO_MANY_REQUESTS
        response.body == 'Request send & Pong throttled it'
    }

    def "should handle other exceptions gracefully"() {
        given:
        WebClient.RequestHeadersUriSpec uriSpec = Mock()
        WebClient.ResponseSpec responseSpec = Mock()

        def exception = new WebClientResponseException(HttpStatus.BAD_GATEWAY.getReasonPhrase(), HttpStatus.BAD_GATEWAY.value(), HttpStatus.BAD_GATEWAY.getReasonPhrase(), null, null, null)
        webClient.get() >> uriSpec
        uriSpec.uri('/pong') >> uriSpec
        uriSpec.header('X-Ping-Service-Name', 'ping-service') >> uriSpec
        uriSpec.retrieve() >> responseSpec
        responseSpec.bodyToMono(String.class) >> Mono.error(exception)

        when:
        ResponseEntity<String> response = controller.sendRequestToPongService().block()

        then:
        response.statusCode == HttpStatus.INTERNAL_SERVER_ERROR
        response.body == 'Internal Server Error'
    }

    def "should return INTERNAL_SERVER_ERROR response when webClient throws Exception"() {
        given:
        def exception = new RuntimeException("Internal Server Error")
        WebClient.RequestHeadersUriSpec uriSpec = Mock()
        WebClient.ResponseSpec responseSpec = Mock()

        webClient.get() >> uriSpec
        uriSpec.uri('/pong') >> uriSpec
        uriSpec.header('X-Ping-Service-Name', 'ping-service') >> uriSpec
        uriSpec.retrieve() >> responseSpec
        responseSpec.bodyToMono(String.class) >> Mono.error(exception)

        when:
        ResponseEntity<String> response = controller.sendRequestToPongService().block()

        then:
        response.statusCode == HttpStatus.INTERNAL_SERVER_ERROR
        response.body == 'Internal Server Error'
    }
}

